use super::*;
use lexer::*;

// ## 6. Glyph positioning (GPOS) rules

// ### 6.a. [GPOS LookupType 1] Single adjustment positioning
// ### 6.b. [GPOS LookupType 2] Pair adjustment positioning
// #### 6.b.i. Specific and class pair kerning
// #### 6.b.ii. Enumerating pairs
// #### 6.b.iii. Subtable breaks
// ### 6.c. [GPOS LookupType 3] Cursive attachment positioning
// ### 6.d. [GPOS LookupType 4] Mark-to-Base attachment positioning
// ### 6.e. [GPOS LookupType 5] Mark-to-Ligature attachment positioning
// ### 6.f. [GPOS LookupType 6] Mark-to-Mark attachment positioning
// ### 6.g. [GPOS LookupType 7] Contextual positioning
// ### 6.h. [GPOS LookupType 8] Chaining contextual positioning
// #### 6.h.i. Specifying a Chain Positioning rule and marking sub-run
// #### 6.h.ii. Specifying Contextual Positioning with explicit lookup references
// #### 6.h.iii. Specifying Contextual Positioning with in-line single positioning rules
// #### 6.h.iv. Specifying Contextual Positioning with in-line cursive positioning rules
// #### 6.h.v. Specifying Contextual Positioning with in-line mark attachment positioning rules
// #### 6.h.vi. Specifying exceptions to the Chain Pos rule
// ### 6.i. [GPOS LookupType 9] Extension positioning
#[derive(Debug, Clone)]
pub struct Position {
	pub items: Vec<PosItem>,
	pub ignore: bool,
	pub is_enum: bool,
}
impl Position {
	pub fn parse(r: &mut Lexer<'_>) -> Result<Self, Error> {
		let mut items = vec![];
		loop {
			match r.read_next()? {
				Event::Semi => break,
				Event::Comma => items.push(PosItem::Comma),
				//
				Event::BASE => items.push(PosItem::Base),
				Event::LIGATURE => items.push(PosItem::Ligature),
				Event::CursiveKw => items.push(PosItem::Cursive),
				Event::MarkKw => items.push(PosItem::Mark),
				//
				Event::Ident(v) => items.push(PosItem::Class(Class(vec![ClassItem::Glyph(v.into())]))),
				Event::Group(v) => items.push(PosItem::Class(Class(vec![ClassItem::Ref(v.into())]))),
				Event::LSquare => items.push(PosItem::Class(Class::parse(r)?)),
				Event::NullKw => items.push(PosItem::Class(Class(vec![]))),
				//
				Event::LAngle => match r.peek_many().read_next()? {
					Event::AnchorKw => items.push(PosItem::Anchor(Anchor::parse(r)?)),
					_ => items.push(PosItem::Value(ValueRecord::parse_value(r)?)),
				},
				Event::Number(v) => items.push(PosItem::Value(ValueRecord::Metric(v.into()))),
				//
				Event::SingleQuote => {
					let last = items.pop().ok_or(Error::Msg("quote must after glyphseq"))?;
					match last {
						PosItem::Class(seq) => items.push(PosItem::Quote(seq)),
						_ => return Err(Error::Msg("quote must after glyphseq")),
					}
				}
				Event::LookupKw => {
					let last = items.pop().ok_or(Error::Msg("lookup must after quote"))?;
					match last {
						PosItem::Quote(seq) => items.push(PosItem::LookUp(seq, vec![r.expect_ident()?.into()])),
						PosItem::LookUp(seq, mut lookups) => {
							lookups.push(r.expect_ident()?.into());
							items.push(PosItem::LookUp(seq, lookups));
						}
						_ => return Err(Error::Msg("lookup must after quote")),
					}
				}
				e => return Err(Error::Unexpect(format!("{:?}", e))),
			}
		}
		Ok(Self {
			items,
			ignore: false,
			is_enum: false,
		})
	}
	pub fn parse_ignore(r: &mut Lexer<'_>) -> Result<Vec<Self>, Error> {
		let this = Self::parse(r)?;
		Ok(this
			.items
			.split(|v| matches!(v, PosItem::Comma))
			.map(|items| Self {
				items: items.to_owned(),
				ignore: true,
				is_enum: false,
			})
			.collect())
	}
	pub fn parse_enum(r: &mut Lexer<'_>) -> Result<Self, Error> {
		let mut this = Self::parse(r)?;
		this.is_enum = true;
		Ok(this)
	}
}
#[derive(Debug, Clone)]
pub enum PosItem {
	Class(Class),
	Quote(Class),
	LookUp(Class, Vec<String>),
	//
	Value(ValueRecord),
	Anchor(Anchor),
	//
	Cursive,
	Base,
	Ligature,
	LigComponent,
	Mark,
	//
	Comma,
}

#[cfg(test)]
mod tests {
	use super::*;
	type P = Position;
	#[test]
	fn pos_single() {
		// position <glyph|glyphclass> <valuerecord>;
		let _v = P::parse(&mut Lexer::new("one <-80 0 -160 0>;")).unwrap();
		// println!("{:#?}", _v);
	}
	#[test]
	fn pos_pair() {
		// position <glyph|glyphclass> <valuerecord> <glyph|glyphclass> <valuerecord>;
		// ##### Pair Pos format A:
		let _v = P::parse(&mut Lexer::new("T -60 a <-40 0 -40 0>;")).unwrap();
		// position <glyph|glyphclass> <glyph|glyphclass> <valuerecord>; # for first <glyph|glyphclass>
		// ##### Pair Pos format B:
		let _v = P::parse(&mut Lexer::new("T a -100;")).unwrap();
		let _v = P::parse(&mut Lexer::new("[T] a -100;")).unwrap();
		let _v = P::parse(&mut Lexer::new("T @a -100;")).unwrap();
		let _v = P::parse(&mut Lexer::new("@T [a o u] -80;")).unwrap();
		// #### 6.b.ii. Enumerating pairs
		_ = Fea::parse("", &mut Lexer::new("feature kern {enum pos @Y_LC semicolon -80;} kern;")).unwrap();
		// #### 6.b.iii. Subtable breaks
		_ = Fea::parse("", &mut Lexer::new("feature kern {subtable;} kern;")).unwrap();
	}
	#[test]
	fn pos_cursive() {
		// position cursive
		//     <glyph|glyphclass>
		//     <anchor>   # Entry anchor
		//     <anchor>;  # Exit anchor
		let content = "feature kern {position cursive meem.medial <anchor 500 20> <anchor 0 -20>;} kern;";
		_ = Fea::parse("", &mut Lexer::new(content)).unwrap();
		let content = "feature kern {position cursive meem.end <anchor 500 20> <anchor NULL>;} kern;";
		_ = Fea::parse("", &mut Lexer::new(content)).unwrap();
	}
	#[test]
	fn pos_mark_to_base() {
		// position base <glyph|glyphclass> # base glyph(s)
		//     # Anchor and mark glyph class, repeated for each
		//     # attachment point on the base glyph(s)
		//     <anchor> mark <named mark glyphclass> +
		// ;
		let content = "base [a e o u] <anchor 250 450> mark @TOP_MARKS <anchor 250 -10> mark @BOTTOM_MARKS;";
		_ = P::parse(&mut Lexer::new(content)).unwrap();
	}
	#[test]
	fn pos_mark_to_ligature() {
		// position ligature <glyph|glyphclass>   # ligature glyph or glyph class
		//     # Anchor and named mark glyph class, repeated for
		//     # each anchor point on the first component glyph:
		//     <anchor> mark <named mark glyph class> +
		//
		//     # Start of anchor and mark info for the next ligature component
		//     ligComponent
		//
		//     # Anchor and named mark glyph class, repeated for
		//     # each anchor point on the next component glyph:
		//     <anchor> mark <named mark glyph class> +
		//
		//     # Additional blocks of ligComponent plus anchor and named mark glyph class
		// ;
		let content = "ligature lam_meem_jeem <anchor 625 1800> mark @TOP_MARKS ligComponent <anchor 376 -368> mark @BOTTOM_MARKS ligComponent <anchor NULL>;";
		_ = P::parse(&mut Lexer::new(content)).unwrap();
	}
	#[test]
	fn pos_mark_to_mark() {
		// position mark <glyph|glyphclass> # mark glyph(s)
		//     # Anchor and mark glyph class, repeated for each
		//     # attachment point on the mark glyph(s)
		//     <anchor> mark <named mark glyphclass> +
		// ;
		let content = "mark hamza <anchor 221 301> mark @MARK_CLASS_1;";
		_ = P::parse(&mut Lexer::new(content)).unwrap();
	}
	#[test]
	fn pos_ignore() {
		_ = Fea::parse("", &mut Lexer::new("feature kern {ignore position [a e o u],[a e o u];} kern;")).unwrap();
	}
	#[test]
	fn pos_contextual() {
		let content = r#"
feature kern {
	#### 6.h.ii. Specifying Contextual Positioning with explicit lookup references
	position T' lookup CNTXT_PAIR_POS [o c]' @ALL_MARKS' lookup CNTXT_MARK_TO_BASE;

	#### 6.h.iii. Specifying Contextual Positioning with in-line single positioning rules
	position [quoteleft quotedblleft ][Y T]' <0 0 20 0> [quoteright quotedblright];
	position [quoteleft quotedblleft ][Y T]' 20 [quoteright quotedblright];
	position s f' 10 t;
	position s f' 10 t' -5 period;
	position L quoteright -150;
	position quoteright A -120;
	position L' 50 quoteright' 70 A;
	position L' -100 quoteright' -50 A;
	position L' -150 quoteright;
	position quoteright' -120 A;
	position s f' 10 t period;
	position L' -100 quoteright' -50 A;
	position L' quoteright -150;  # special cases of contextual positioning
	position quoteright' A -120;  # where value record follows unmarked glyph,
	position s f' t 10 period;    # making them exactly equivalent to 3B.
	position L' quoteright' -150;
	
	#### 6.h.iv. Specifying Contextual Positioning with in-line cursive positioning rules
	position @BACKTRACK_GLYPHS_FOR_MEEM cursive meem.medial' <anchor 500 20> <anchor 0 -20> @LOOKAHEAD_GLYPHS_FOR_MEEM;
	
	#### 6.h.v. Specifying Contextual Positioning with in-line mark attachment positioning rules
	position [T V F] base [a e o u] <anchor 250 450> mark @TOP_MARKS' <anchor 250 -10> mark @BOTTOM_MARKS' @VOWELS;
} kern;
"#;
		_ = Fea::parse("", &mut Lexer::new(content)).unwrap();
	}
}
